package report5;

import report5.classes.Disk;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Random;

/**
 * 计算类
 *
 * @author zouran
 * createDate：2023/5/25  18:19
 */
public class Calculate {
    public final static int SCAN_UP = 1;
    public final static int SCAN_COMMON = 0;
    public final static int SCAN_DOWN = -1;
    public final static int SCAN = 1;
    public final static int CSCAN = 2;
    //记录访问序列
    private final List<Disk> requestList = new ArrayList<>();
    //开始位置
    private final int begin;
    //记录访问结果
    private List<Integer> resultList = new ArrayList<>();
    //访问序列长度
    private int length = 10;
    //记录当前磁头位置,SSTF中用到
    private int current;

    //随机序列
    public Calculate(int length) {
        this.length = length;
        randomList();
        //设置初始指针位置
        this.current = this.requestList.get(0).getPosition();
        this.begin = this.requestList.get(0).getPosition();
    }

    //输出序列
    public void getRequestList() {
        System.out.print("磁盘访问序列为：");
        for (Disk disk : requestList) {
            System.out.printf(disk.getPosition() + ",");
        }
        System.out.print("。。。(逗号间隔)\n");
    }

    //随机访问序列,需要确保无重复值
    private void randomList() {
        Random random = new Random();
        boolean[] hash = new boolean[200];
        while (requestList.size() < length) {
            int randInt = random.nextInt(0, 200);
            if (!hash[randInt]) {
                hash[randInt] = true;
                requestList.add(new Disk(randInt, false));
            }
        }
    }

    //找到最短路径的磁盘,传入值进行控制扫描方向,考虑俩边一样的情况？
    private int findShortestDisk(int direction) {
        int index = 0, minDistance = Integer.MAX_VALUE;
        for (Disk disk : requestList) {
            //根据方向进行增加判别条件，可封装为方法
            if ((direction == SCAN_UP && disk.getPosition() >= current)
                    || (direction == SCAN_DOWN && disk.getPosition() <= current)
                    || (direction == SCAN_COMMON))
                //未访问
                if (disk.isNotFinished()) {
                    int distance = Math.abs(disk.getPosition() - current);
                    //最近的
                    if (distance < minDistance) {
                        minDistance = distance;
                        index = requestList.indexOf(disk);
                    }
                }
        }
        return index;
    }

    //判断是否都已经访问
    private boolean isNotAllFinished(int direction) {
        for (Disk disk : requestList) {
            //根据方向进行增加判别条件，可封装为方法
            if ((direction == SCAN_UP && disk.getPosition() >= current)
                    || (direction == SCAN_DOWN && disk.getPosition() <= current)
                    || (direction == SCAN_COMMON))
                if (disk.isNotFinished()) {
                    return true;
                }
        }
        return false;
    }

    //直接按顺序加入
    public void FCFS() {
        requestList.forEach(disk -> resultList.add(disk.getPosition()));
        print("FCFS", SCAN_UP);
    }

    //SSTF算法
    public void SSTF() {
        while (isNotAllFinished(SCAN_COMMON)) {
            int index = findShortestDisk(SCAN_COMMON);
            //设置已经访问
            requestList.get(index).setFinished(true);
            //添加到结果集
            resultList.add(requestList.get(index).getPosition());
            //更新当前指针
            current = requestList.get(index).getPosition();
        }
        print("SSTF", SCAN_UP);
    }

    //扫描算法
    public void SCAN_OR_CSCAN(int direction, int option) {
        while (isNotAllFinished(SCAN_COMMON)) {
            while (isNotAllFinished(direction)) {
                int index = findShortestDisk(direction);
                //设置已经访问
                requestList.get(index).setFinished(true);
                //添加到结果集
                resultList.add(requestList.get(index).getPosition());
                //更新当前指针
                current = requestList.get(index).getPosition();
            }
            if (option == SCAN) {
                //反向
                direction = -direction;
            }
            //不变向
            else if (option == CSCAN) {
                int base = 0;
                //由高到低找最大值
                if (direction == SCAN_DOWN) {
                    base = Integer.MIN_VALUE;
                }//由低到高找最小值
                else if (direction == SCAN_UP) {
                    base = Integer.MAX_VALUE;
                }
                for (Disk disk : requestList) {
                    if (disk.isNotFinished()) {
                        if ((direction == SCAN_UP && disk.getPosition() < base)
                                || direction == SCAN_DOWN && disk.getPosition() > base)
                            base = disk.getPosition();
                    }
                }
                current = base;
            }
        }
        switch (option) {
            case SCAN:
                print("SCAN", direction);
                break;
            case CSCAN:
                print("CSCAN", direction);
                break;
        }
    }

    private void print(String name, int direction) {
        System.out.printf("您选择了%s算法，当前磁头运动方向为由低到高，执行结果如下：\n", name);
        System.out.println("当前磁道号\t下一磁道号\t绝对差");
        double length = 0.0;
        for (int i = 0; i < resultList.size() - 1; i++) {
            length += Math.abs(resultList.get(i + 1) - resultList.get(i));
            System.out.printf("%d\t%d\t%d\n", resultList.get(i), resultList.get(i + 1), Math.abs(resultList.get(i + 1) - resultList.get(i)));
        }
        System.out.printf("%d\t。。。\t。。。\n", resultList.get(resultList.size() - 1));
        String string = "";
        if (Objects.equals(name, "SCAN") || Objects.equals(name, "CSCAN")) {
            if (direction == SCAN_UP) string = "由低到高";
            else string = "由高到低";
        }
        System.out.printf("寻道序列总长%d，%s%s算法的平均寻道长度为%f。\n", resultList.size(), name, string, length / resultList.size());
        //输出完后清除本次
        resultList.clear();
        requestList.forEach(disk -> disk.setFinished(false));
        current = begin;
    }
}
